#ifndef CFG_ITEMS_HPP
#define CFG_ITEMS_HPP

#include "config.h"
#include <string>
#include <list>
#include <vector>
#include <memory>
#include <ostream>
#include <functional>

#define DEF_NEW(CLS) \
template <typename ... Args> \
static std::shared_ptr<CLS> New(Args&& ... args) \
{ \
    return std::make_shared<CLS>(std::forward<Args>(args)...); \
}

struct DIARY_APP_SDK_API IValueAdapter {
    virtual ~IValueAdapter();

    virtual std::string get() const = 0;
    virtual bool set(const std::string &input, std::ostream &err_output) = 0;
};

enum struct CfgType {
    Group,
    Text,
    Integral,
    FloatingPoint,
    Switch,
    Choice,
    Action,
};

struct CfgItemBase;
using CfgItemPtr = std::shared_ptr<CfgItemBase>;
using RenderringFunc = void(*)(CfgItemBase*);

struct DIARY_APP_SDK_API CfgItemBase {
    
    CfgItemBase(CfgType type, std::string title, std::string help = {}, bool enabled = true);
    virtual ~CfgItemBase();


    void    Render();
    virtual bool Enabled() const;
    virtual void Load();
    virtual bool Save(std::ostream &err_out);
    virtual bool Save();

    CfgType     const type_;
    std::string const title_;
    std::string const title_hidden_;
    std::string const help_;
    bool        const enabled_;

    static  RenderringFunc pFnRender; // 由UI实现如何绘制
};

struct DIARY_APP_SDK_API CfgGroup : CfgItemBase {
    CfgGroup(std::string title, const std::initializer_list<CfgItemPtr>& children, std::string help = {}, bool enabled = true);
    CfgGroup(std::string title = {}, std::string help = {}, bool enabled = true);
    ~CfgGroup() override;

    void AddItem(CfgItemPtr child);
    void AddItems(const std::initializer_list<CfgItemPtr>& children);
    void Load() override;
    bool Save(std::ostream &err_out) override;

    bool                  show_title_;
    std::list<CfgItemPtr> children_;

    DEF_NEW(CfgGroup)
};

struct DIARY_APP_SDK_API CfgEditable : CfgItemBase {
    CfgEditable(IValueAdapter *adapter, CfgType type, std::string title, std::string help = {}, bool enabled = true);
    ~CfgEditable() override;

    void Load() override;
    bool Save(std::ostream &err_out) override;

    IValueAdapter *adapter_;
    std::string vcache_;
};

struct DIARY_APP_SDK_API CfgAction : CfgItemBase {
    using Callback = std::function<void()>;

    CfgAction(Callback cb, bool sameline, std::string title, std::string help = {}, bool enabled = true);
    ~CfgAction() override;

    bool     sameline_;
    Callback callback_;

    DEF_NEW(CfgAction)
};

struct DIARY_APP_SDK_API CfgText : CfgEditable {
    CfgText(IValueAdapter *adapter, std::string title, std::string help = {}, bool enabled = true);
    ~CfgText() override;

    DEF_NEW(CfgText)
};

struct DIARY_APP_SDK_API CfgIntegral : CfgEditable {
    CfgIntegral(bool is_signed, IValueAdapter *adapter, std::string title, std::string help = {}, bool enabled = true);
    ~CfgIntegral() override;

    void Load() override;
    bool Save(std::ostream &err_out) override;

    bool  is_signed_;
    union {
        int64_t  i64_;
        uint64_t u64_;
    };

    DEF_NEW(CfgIntegral)
};

struct DIARY_APP_SDK_API CfgReal : CfgEditable {
    CfgReal(IValueAdapter *adapter, std::string title, std::string help = {}, bool enabled = true);
    ~CfgReal() override;

    void Load() override;
    bool Save(std::ostream &err_out) override;

    double value_;

    DEF_NEW(CfgReal)
};

struct DIARY_APP_SDK_API CfgSwitch : CfgEditable {
    CfgSwitch(std::string on_value, std::string off_value, IValueAdapter *adapter, std::string title, std::string help = {}, bool enabled = true);
    ~CfgSwitch() override;

    void Load() override;
    bool Save(std::ostream &err_out) override;
    
    bool value_;
    std::string const on_value_;
    std::string const off_value_;

    DEF_NEW(CfgSwitch)
};

struct DIARY_APP_SDK_API CfgChoice : CfgEditable {
    CfgChoice(const std::vector<std::string> &options, IValueAdapter *adapter, std::string title, std::string help = {}, bool enabled = true);
    ~CfgChoice() override;

    void Load() override;
    bool Save(std::ostream &err_out) override;

    int32_t selected_;
    std::vector<std::string> options_;

    DEF_NEW(CfgChoice)
};

#undef DEF_NEW

#endif /* CFG_ITEMS_HPP */
